package com.yupi.yuoj.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.yuoj.common.ErrorCode;
import com.yupi.yuoj.exception.BusinessException;
import com.yupi.yuoj.judge.JudgeService;
import com.yupi.yuoj.mapper.ContestSubmitMapper;
import com.yupi.yuoj.model.dto.questionsubmit.QuestionSubmitAddRequest;
import com.yupi.yuoj.model.entity.Contest;
import com.yupi.yuoj.model.entity.ContestQuestion;
import com.yupi.yuoj.model.entity.ContestRegistration;
import com.yupi.yuoj.model.entity.ContestSubmit;
import com.yupi.yuoj.model.entity.Question;
import com.yupi.yuoj.model.entity.QuestionSubmit;
import com.yupi.yuoj.model.entity.User;
import com.yupi.yuoj.model.enums.QuestionSubmitStatusEnum;
import com.yupi.yuoj.service.ContestQuestionService;
import com.yupi.yuoj.service.ContestRegistrationService;
import com.yupi.yuoj.service.ContestService;
import com.yupi.yuoj.service.ContestSubmitService;
import com.yupi.yuoj.service.QuestionService;
import com.yupi.yuoj.service.QuestionSubmitService;
import com.yupi.yuoj.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 竞赛提交服务实现
*/
@Service
public class ContestSubmitServiceImpl extends ServiceImpl<ContestSubmitMapper, ContestSubmit>
        implements ContestSubmitService {

    @Resource
    @Lazy
    private ContestService contestService;

    @Resource
    private ContestQuestionService contestQuestionService;

    @Resource
    @Lazy
    private ContestRegistrationService contestRegistrationService;

    @Resource
    private QuestionService questionService;

    @Resource
    private QuestionSubmitService questionSubmitService;

    @Resource
    private UserService userService;

    @Resource
    private JudgeService judgeService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public long doContestSubmit(Long contestId, QuestionSubmitAddRequest questionSubmitAddRequest, HttpServletRequest request) {
        // 1. 校验竞赛是否存在且进行中
        Contest contest = contestService.getById(contestId);
        if (contest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }
        if (contest.getStatus() != 1) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "竞赛未开始或已结束");
        }

        // 2. 校验题目是否属于该竞赛
        Long questionId = questionSubmitAddRequest.getQuestionId();
        QueryWrapper<ContestQuestion> contestQuestionQueryWrapper = new QueryWrapper<>();
        contestQuestionQueryWrapper.eq("contestId", contestId);
        contestQuestionQueryWrapper.eq("questionId", questionId);
        if (contestQuestionService.count(contestQuestionQueryWrapper) <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "题目不属于该竞赛");
        }

        // 3. 校验用户是否已参加竞赛且处于参与中状态
        User loginUser = userService.getLoginUser(request);
        Long userId = loginUser.getId();
        QueryWrapper<ContestRegistration> registrationQueryWrapper = new QueryWrapper<>();
        registrationQueryWrapper.eq("contestId", contestId);
        registrationQueryWrapper.eq("userId", userId);
        registrationQueryWrapper.eq("status", 1); // 参与中状态
        if (contestRegistrationService.count(registrationQueryWrapper) <= 0) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "您尚未开始该竞赛");
        }

        // 4. 提交题目 (调用原有的题目提交服务)
        long questionSubmitId = questionSubmitService.doQuestionSubmit(questionSubmitAddRequest, loginUser);
        
        // 5. 异步判题完成后，会更新题目的提交状态
        // 需要创建竞赛提交记录
        Question question = questionService.getById(questionId);
        if (question == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
        }
        
        // 计算当前题目的提交次数
        int attempts = getUserQuestionAttempts(contestId, questionId, userId) + 1;
        
        // 创建竞赛提交记录
        ContestSubmit contestSubmit = new ContestSubmit();
        contestSubmit.setContestId(contestId);
        contestSubmit.setQuestionId(questionId);
        contestSubmit.setUserId(userId);
        contestSubmit.setQuestionSubmitId(questionSubmitId);
        contestSubmit.setSubmitTime(new Date());
        contestSubmit.setIsAccepted(0); // 初始为未通过
        contestSubmit.setAttempts(attempts);
        contestSubmit.setPenalty(0); // 暂时设为0，判题后更新
        
        boolean saveResult = this.save(contestSubmit);
        if (!saveResult) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "竞赛提交记录保存失败");
        }
        
        return questionSubmitId;
    }

    @Override
    public ContestSubmit processContestSubmitResult(QuestionSubmit questionSubmit, Question question) {
        if (questionSubmit == null || question == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        
        // 1. 查询是否为竞赛提交
        QueryWrapper<ContestSubmit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("questionSubmitId", questionSubmit.getId());
        ContestSubmit contestSubmit = this.getOne(queryWrapper);
        
        if (contestSubmit == null) {
            // 不是竞赛提交，直接返回null
        return null;
        }
        
        // 2. 判断题目状态，只处理已判题的提交
        Integer status = questionSubmit.getStatus();
        if (!QuestionSubmitStatusEnum.SUCCEED.getValue().equals(status)) {
            return contestSubmit;
        }
        
        // 3. 获取竞赛信息，计算罚时
        Long contestId = contestSubmit.getContestId();
        Contest contest = contestService.getById(contestId);
        if (contest == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "竞赛不存在");
        }
        
        // 4. 解析判题信息，判断是否通过
        String judgeInfo = questionSubmit.getJudgeInfo();
        // 判断是否AC，这里需要根据judgeInfo的格式解析
        boolean isAccepted = judgeInfo != null && judgeInfo.contains("\"message\":\"Accepted\"");
        
        // 5. 更新竞赛提交记录
        contestSubmit.setIsAccepted(isAccepted ? 1 : 0);
        
        // 6. 如果通过，计算罚时
        if (isAccepted) {
            // 获取用户在本题的失败次数
            int failedAttempts = contestSubmit.getAttempts() - 1;
            
            // 根据竞赛规则计算罚时（提交时间 + 失败次数 * 罚时）
            int penaltyMinutes = calculatePenaltyMinutes(contest, contestSubmit.getSubmitTime(), failedAttempts);
            contestSubmit.setPenalty(penaltyMinutes);
        }
        
        // 7. 更新提交记录
        this.updateById(contestSubmit);
        
        return contestSubmit;
    }

    @Override
    public int getUserQuestionAttempts(Long contestId, Long questionId, Long userId) {
        if (contestId == null || questionId == null || userId == null) {
        return 0;
        }
        
        QueryWrapper<ContestSubmit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("questionId", questionId);
        queryWrapper.eq("userId", userId);
        
        return Math.toIntExact(this.count(queryWrapper));
    }

    @Override
    public int getUserSolvedCount(Long contestId, Long userId) {
        if (contestId == null || userId == null) {
        return 0;
        }
        
        QueryWrapper<ContestSubmit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("userId", userId);
        queryWrapper.eq("isAccepted", 1);
        queryWrapper.select("DISTINCT questionId");
        
        return Math.toIntExact(this.count(queryWrapper));
    }

    @Override
    public List<ContestSubmit> getUserContestSubmits(Long contestId, Long userId) {
        if (contestId == null || userId == null) {
            return new ArrayList<>();
        }
        
        QueryWrapper<ContestSubmit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("userId", userId);
        queryWrapper.orderByDesc("submitTime");
        
        return this.list(queryWrapper);
    }

    @Override
    public int calculateUserTotalPenalty(Long contestId, Long userId) {
        if (contestId == null || userId == null) {
        return 0;
        }
        
        // 获取用户在该竞赛中每题首次通过的提交（有罚时）
        QueryWrapper<ContestSubmit> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("contestId", contestId);
        queryWrapper.eq("userId", userId);
        queryWrapper.eq("isAccepted", 1);
        queryWrapper.groupBy("questionId");
        queryWrapper.select("SUM(penalty) as totalPenalty");
        
        // 计算总罚时
        Integer totalPenalty = 0;
        List<ContestSubmit> submits = this.list(queryWrapper);
        for (ContestSubmit submit : submits) {
            totalPenalty += submit.getPenalty();
        }
        
        return totalPenalty;
    }
    
    /**
     * 计算罚时（分钟）
     *
     * @param contest 竞赛
     * @param submitTime 提交时间
     * @param failedAttempts 失败尝试次数
     * @return 罚时分钟数
     */
    private int calculatePenaltyMinutes(Contest contest, Date submitTime, int failedAttempts) {
        // 1. 计算从竞赛开始到提交的分钟数
        long startTimeMillis = contest.getStartTime().getTime();
        long submitTimeMillis = submitTime.getTime();
        long diffMillis = submitTimeMillis - startTimeMillis;
        int timePenalty = (int) (diffMillis / (60 * 1000)); // 转换为分钟
        
        // 2. 计算错误提交的惩罚
        int failurePenalty = failedAttempts * contest.getPenalty();
        
        // 3. 总罚时 = 提交时间 + 错误提交惩罚
        return timePenalty + failurePenalty;
    }
}




